from __future__ import annotations

import hashlib
import hmac
import json
import time
from collections import OrderedDict

import requests
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponse
from django.http import HttpResponseForbidden

from payments import PaymentStatus
from payments.core import BasicProvider


class CoinbaseProvider(BasicProvider):
    """Payment provider for coinbase.

    This backend implements payments using `Coinbase <https://www.coinbase.com/>`_.

    This backend does not support fraud detection.

    :param key: Api key generated by Coinbase
    :param secret: Api secret generated by Coinbase
    :param endpoint: Coinbase endpoint domain to use. For the production
        environment, use ``'coinbase.com'`` instead
    """

    _method = "get"
    api_url = "https://api.%(endpoint)s/v1/buttons"
    checkout_url = "https://%(endpoint)s/checkouts"

    def __init__(self, key, secret, endpoint="sandbox.coinbase.com", **kwargs):
        self.endpoint = endpoint
        self.key = key
        self.secret = secret
        super().__init__(**kwargs)
        if not self._capture:
            raise ImproperlyConfigured("Coinbase does not support pre-authorization.")

    def get_custom_token(self, payment):
        value = f"coinbase-{payment.token}-{self.key}"
        return hashlib.md5(value.encode("utf-8")).hexdigest()

    def get_checkout_code(self, payment):
        api_url = self.api_url % {"endpoint": self.endpoint}
        button_data = {
            "name": payment.description,
            "price_string": str(payment.total),
            "price_currency_iso": payment.currency,
            "callback_url": self.get_return_url(payment),
            "success_url": payment.get_success_url(),
            "cancel_url": payment.get_failure_url(),
            "custom": self.get_custom_token(payment),
        }
        # ordered dict for stable JSON output
        data = {"button": OrderedDict(sorted(button_data.items()))}
        nonce = int(time.time() * 1e6)
        message = str(nonce) + api_url + json.dumps(data)
        signature = hmac.new(
            self.secret.encode(), message.encode(), hashlib.sha256
        ).hexdigest()
        headers = {
            "ACCESS_KEY": self.key,
            "ACCESS_SIGNATURE": signature,
            "ACCESS_NONCE": nonce,
            "Accept": "application/json",
        }
        response = requests.post(api_url, data=json.dumps(data), headers=headers)

        response.raise_for_status()
        results = response.json()
        return results["button"]["code"]

    def get_action(self, payment):
        checkout_url = self.checkout_url % {"endpoint": self.endpoint}
        return f"{checkout_url}/{self.get_checkout_code(payment)}"

    def get_hidden_fields(self, payment):
        return {}

    def process_data(self, payment, request):
        try:
            results = json.loads(request.body)
        except (ValueError, TypeError):
            return HttpResponseForbidden("FAILED")

        if results["order"]["custom"] != self.get_custom_token(payment):
            return HttpResponseForbidden("FAILED")

        if payment.status == PaymentStatus.WAITING:
            payment.transaction_id = results["order"]["transaction"]["id"]
            payment.change_status(PaymentStatus.CONFIRMED)
            payment.save()
        return HttpResponse("OK")
